package com.swak.fx.support;

import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.swak.App;
import com.swak.config.setting.Settings;
import com.swak.entity.Wrap;
import com.swak.fx.support.Toast.ToastConfig;
import com.swak.fx.support.ui.UI;
import com.swak.fx.support.ui.UI.Animations;
import com.swak.fx.support.ui.UI.Themes;
import com.swak.fx.support.ui.UIController;
import com.swak.fx.support.ui.UIEvent;
import com.swak.fx.support.ui.UILifecycle;
import com.swak.reactivex.threads.TimerContext.ScheduledTimerTask;
import com.swak.ui.Event;
import com.swak.ui.Events;
import com.swak.utils.Maps;
import com.swak.utils.StringUtils;
import com.swak.utils.time.DateTimes;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.TextInputControl;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import lombok.Getter;

/**
 * 基本的 View
 * 
 * @author 618lf
 */
public abstract class AbstractView implements Lifecycle, UILifecycle, UIController {

	public static final Logger LOGGER = LoggerFactory.getLogger(AbstractView.class);

	protected final AtomicBoolean destoryed = new AtomicBoolean(false);

	@FXML
	@Getter
	protected Region root;

	protected final FXMLView annotation;
	protected final Settings settings;
	protected final FXMLRender fxmlRender;

	public AbstractView() {

		// 初始化参数
		this.settings = Settings.intSettings();

		// 初始化界面 - 会设置根节点
		this.fxmlRender = new FXMLRender(this, settings);
		this.annotation = this.fxmlRender.getAnnotation();
		this.fxmlRender.render();

		// 设置根节点自适应
		this.useComputedSize();

		// 自动注入依赖
		App.autowireBean(this);
	}

	protected void useComputedSize() {
		this.root.setMinHeight(-1);
		this.root.setMaxHeight(-1);
		this.root.setMinWidth(-1);
		this.root.setMaxWidth(-1);
		this.root.setPrefHeight(-1);
		this.root.setPrefWidth(-1);
	}

	@Override
	public List<String> getStylesheets() {
		return this.root.getStylesheets();
	}

	protected void addChild(Node child) {
		if (this.root instanceof Pane) {
			((Pane) this.root).getChildren().add(child);
		}
	}

	protected void removeChild(Node child) {
		if (this.root instanceof Pane && child != null) {
			if (this.enableChildRemoveAnimation.get()) {
				Animations.ZoomOut.New(child).setOnFinished((e) -> {
					this.doRemoveChild(child);
				}).play();
			} else {
				this.doRemoveChild(child);
			}
		}
	}

	private void doRemoveChild(Node child) {
		((Pane) this.root).getChildren().remove(child);
	}

	protected void clearChild() {
		if (this.root instanceof Pane) {
			((Pane) this.root).getChildren().clear();
		}
	}

	protected void addStyleClass(String styleClass) {
		this.root.getStyleClass().add(styleClass);
	}

	@Override
	public void setupValueChangedListeners() {
		this.root.managedProperty().addListener((ob, o, n) -> {
			if (n) {
				this.mounted();
			} else {
				this.unmounted();
			}
		});
		this.root.visibleProperty().addListener((ob, o, n) -> {
			if (n) {
				this.activated();
			} else {
				this.deactivated();
			}
		});
	}

	@Override
	public void initializeFinish() {
		if (this.root.isManaged()) {
			this.mounted();
		}
		if (this.root.isManaged() && this.root.isVisible()) {
			this.activated();
		}
	}

	protected String getPageConfig(String key, String defaultValue) {
		return StringUtils.defaultIfBlank(
				this.settings.getConfig(
						UI.Conts.Settings_Pref + StringUtils.lowerCaseFirstOne(this.getClass().getSimpleName()) + key),
				defaultValue);
	}

	protected void setPageConfig(String key, String value) {
		this.settings.putConfig(
				UI.Conts.Settings_Pref + StringUtils.lowerCaseFirstOne(this.getClass().getSimpleName()) + key, value);
	}

	protected void storePageConfig() {
		this.settings.storeConfig();
	}

	public void exitWithError(Throwable error) {
		Display.runOnUI(() -> {
			Dialogs.error(error).ifPresent(response -> Events.post(Event.EXIT));
		});
	}

	@Override
	public void windowStateChange(int state) {
		// this.windowStateActivatedChange(state);
	}

	// 不能通过此方法传递事件
//	protected void windowStateActivatedChange(int state) {
//		if (UI.States.isWindowFront(state) && this.root.isVisible() && this.root.isManaged()) {
//			this.activated();
//		} else if (this.root.isVisible() && this.root.isManaged()) {
//			this.deactivated();
//		}
//	}

	@Override
	public boolean isDestoryed() {
		return this.destoryed.get();
	}

	@Override
	public void destory() {
		if (this.destoryed.compareAndExchange(false, true)) {
			return;
		}
		try {
			if (this.handlers != null) {
				this.handlers.clear();
			}
		} catch (Exception e) {
		}
	}

	/**
	 * 得到窗口
	 * 
	 * @return
	 */
	protected javafx.stage.Window getWindow() {
		return this.root.getScene().getWindow();
	}

	/**
	 * 获得屏幕
	 * 
	 * @return
	 */
	protected Screen getScreen() {
		return (Screen) this.root.getScene().getWindow().getProperties().get(UI.Conts.CurrentScreen_Key);
	}

	/**
	 * 获得屏幕
	 * 
	 * @return
	 */
	protected Screen getScreen(javafx.stage.Window win) {
		return (Screen) win.getProperties().get(UI.Conts.CurrentScreen_Key);
	}

	/**
	 * 获得屏幕
	 * 
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected List<Screen> getScreens() {
		return (List<Screen>) this.root.getScene().getWindow().getProperties().get(UI.Conts.CurrentScreens_Key);
	}

	/**
	 * 获得屏幕
	 * 
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected List<Screen> getScreens(javafx.stage.Window win) {
		return (List<Screen>) win.getProperties().get(UI.Conts.CurrentScreens_Key);
	}

	// ================================================================================
	// Worker
	// ================================================================================
	protected void $run(Runnable task) {
		AbstractApplication.INSTANCE.get$context().execute(task);
	}

	protected void $runUI(Runnable task) {
		Display.runUI(task);
	}

	protected void $runOnUI(Runnable task) {
		Display.runOnUI(task);
	}

	protected <T> CompletableFuture<T> $runContextCompleteWithUI(Callable<CompletableFuture<T>> task) {
		CompletableFuture<T> future = new CompletableFuture<>();
		this.$run(() -> {
			try {
				CompletableFuture<T> callback = task.call();
				callback.whenComplete((r, e) -> {
					Display.runUI(() -> {
						future.complete(r);
					});
				});
			} catch (Exception ex) {
				LOGGER.error("$runContextCompleteWithUI Error, {}", ex);
				Display.runUI(() -> {
					future.completeExceptionally(ex);
				});
			}
		});
		return future;
	}

	protected <T> CompletableFuture<T> $runUICompleteWithContext(Callable<T> task) {
		CompletableFuture<T> future = new CompletableFuture<>();
		Display.runUI(() -> {
			Wrap<T> res = new Wrap<>();
			try {
				res.setValue(task.call());
			} catch (Exception e) {
				LOGGER.error("$runUI Error, {}", e);
			}
			this.$run(() -> {
				future.complete(res.getValue());
			});
		});
		return future;
	}

	// ================================================================================
	// Timer
	// ================================================================================
	protected ScheduledTimerTask $setTimeOut(Runnable task, long delayMs) {
		return AbstractApplication.INSTANCE.get$timer().schedule(
				() -> AbstractApplication.INSTANCE.get$context().execute(task), delayMs, TimeUnit.MILLISECONDS);
	}

	protected ScheduledTimerTask $setTimeInterval(Runnable task, long period) {
		return AbstractApplication.INSTANCE.get$timer().scheduleAtFixedRate(
				() -> AbstractApplication.INSTANCE.get$context().execute(task), period, TimeUnit.MILLISECONDS);
	}

	// ================================================================================
	// 消息 - 提醒 - 事件
	// ================================================================================
	protected Toast $toast(String success) {
		return this.$toast(ToastConfig.of().setMessage(success).setTheme(Themes.Success));
	}

	protected Toast $toast(ToastConfig config) {
		Toast toast = new Toast(this.root.getScene().getWindow(), config);
		if (config.getDuration() != null && config.getDuration() > 0) {
			this.$setTimeOut(() -> {
				this.$runOnUI(() -> {
					toast.hide();
				});
			}, config.getDuration());
		}
		return toast;
	}

	protected void $post(Event event) {
		Events.post(event);
	}

	protected void $post(String key, Object message) {
		Events.post(Event.of().setKey(key).setMessage(message));
	}

	// ================================================================================
	// 组件的事件
	// ================================================================================
	private Map<String, Consumer<Object>> handlers = Maps.newHashMap();

	public void $emit(String event) {
		this.$emit(event, null);
	}

	public void $emit(String event, Object param) {
		Consumer<Object> handler = this.handlers.get(event);
		if (handler != null) {
			handler.accept(param);
		}
	}

	public void $on(String event, Consumer<Object> handler) {
		this.handlers.put(event, handler);
	}

	// ================================================================================
	// 全局的窗口事件: （注意：Button 不支持这种方式，因为按钮拦截了MOUSE_CLICKED处理）
	// ================================================================================
	protected void registerUIEvent(UI.Events event) {
		this.registerUIEvent(root, event);
	}

	protected void registerUIEvent(Node node, UI.Events event) {
		node.getProperties().put(event.getBindKey(), true);
	}

	protected void unregisterUIEvent(UI.Events event) {
		this.unregisterUIEvent(root, event);
	}

	protected void unregisterUIEvent(Node node, UI.Events event) {
		node.getProperties().remove(event.getBindKey());
	}

	protected void registerUIEventUseFireCloseEvent(Node node, UI.Events event) {
		if (event == UI.Events.Window_Close) {
			node.addEventHandler(MouseEvent.MOUSE_CLICKED, UIEvent.Window_Close_Transmiter);
		}
	}

	protected void unregisterUIEventUseFireCloseEvent(Node node, UI.Events event) {
		if (event == UI.Events.Window_Close) {
			node.removeEventHandler(MouseEvent.MOUSE_CLICKED, UIEvent.Window_Close_Transmiter);
		}
	}

	protected void registerRootClickToRequestFocusEvent_WithoutInput() {
		this.root.addEventHandler(MouseEvent.MOUSE_CLICKED, (e) -> {
			if (!(e.getTarget() instanceof TextInputControl)) {
				this.root.requestFocus();
			}
		});
	}

	protected AtomicLong longPressTime = new AtomicLong(0);
	protected static final long longPressDefineMs = 2 * 1000; // 3s

	protected void registerMouseLeftBtnLongPressEvent(Runnable run) {
		this.root.addEventHandler(MouseEvent.MOUSE_PRESSED, (e) -> {
			if (e.getButton() != null && e.getButton() == MouseButton.PRIMARY) {
				longPressTime.set(DateTimes.nowMilli());
			}
		});
		this.root.addEventHandler(MouseEvent.MOUSE_RELEASED, (e) -> {
			if (e.getButton() != null && e.getButton() == MouseButton.PRIMARY) {
				e.consume();
				long now = DateTimes.nowMilli();
				if (now - longPressTime.get() >= longPressDefineMs) {
					longPressTime.set(DateTimes.nowMilli());
					run.run();
				}
			}
		});
	}

	// ================================================================================
	// 对所属窗体的操作
	// ================================================================================
	protected void closeWindow() {
		this.root.fireEvent(UIEvent.of().setCmd(UI.Events.Window_Close.getBindKey()));
	}

	protected void focusWindow() {
		this.getWindow().requestFocus();
	}

	protected void glintWindow() {
		this.getWindow().requestFocus();
	}

	// ================================================================================
	// 属性
	// ================================================================================
	protected BooleanProperty enableChildRemoveAnimation = new SimpleBooleanProperty(false);

	public BooleanProperty enableChildRemoveAnimation() {
		return enableChildRemoveAnimation;
	}
}
